package com.scs.application.modules.order.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.core.exceptions.BusinessException;
import com.scs.application.core.utils.BeanUtils;
import com.scs.application.core.utils.DateUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.core.utils.UserUtils;
import com.scs.application.modules.base.entity.HospitalConfig;
import com.scs.application.modules.base.entity.HospitalMaterial;
import com.scs.application.modules.base.request.HospitalMaterialRequest;
import com.scs.application.modules.base.service.HospitalConfigService;
import com.scs.application.modules.base.service.HospitalMaterialService;
import com.scs.application.modules.order.consts.DictConsts;
import com.scs.application.modules.order.dto.DeliveryDTO;
import com.scs.application.modules.order.dto.DeliveryItemDTO;
import com.scs.application.modules.order.entity.*;
import com.scs.application.modules.order.mapper.*;
import com.scs.application.modules.order.service.DeliveryItemSnService;
import com.scs.application.modules.order.service.ICreateDelivery;
import com.scs.application.modules.order.service.IDelivery;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 */
@Slf4j
@Service
public class DeliveryImpl implements IDelivery {

    private DeliveryMapper deliveryMapper;
    private DeliveryItemMapper deliveryItemMapper;
    private DeliveryItemSnService deliveryItemSnService;
    private HospitalConfigService hospitalConfigService;
    private HospitalMaterialService hospitalMaterialService;
    private PurchaseOrderItemMapper purchaseOrderItemMapper;
    private PurchaseOrderMapper purchaseOrderMapper;
    private PurchaseProcessMapper purchaseProcessMapper;
    private ICreateDelivery createDelivery;

    @Autowired
    public void setDeliveryMapper(DeliveryMapper deliveryMapper) {
        this.deliveryMapper = deliveryMapper;
    }

    @Autowired
    public void setDeliveryItemMapper(DeliveryItemMapper deliveryItemMapper) {
        this.deliveryItemMapper = deliveryItemMapper;
    }

    @Autowired
    public void setDeliveryItemSnService(DeliveryItemSnService deliveryItemSnService) {
        this.deliveryItemSnService = deliveryItemSnService;
    }

    @Autowired
    public void setHospitalConfigService(HospitalConfigService hospitalConfigService) {
        this.hospitalConfigService = hospitalConfigService;
    }

    @Autowired
    public void setHospitalMaterialService(HospitalMaterialService hospitalMaterialService) {
        this.hospitalMaterialService = hospitalMaterialService;
    }

    @Autowired
    public void setPurchaseOrderItemMapper(PurchaseOrderItemMapper purchaseOrderItemMapper) {
        this.purchaseOrderItemMapper = purchaseOrderItemMapper;
    }

    @Autowired
    public void setPurchaseOrderMapper(PurchaseOrderMapper purchaseOrderMapper) {
        this.purchaseOrderMapper = purchaseOrderMapper;
    }

    @Autowired
    public void setPurchaseProcessMapper(PurchaseProcessMapper purchaseProcessMapper) {
        this.purchaseProcessMapper = purchaseProcessMapper;
    }

    @Autowired
    public void setCreateDelivery(ICreateDelivery createDelivery) {
        this.createDelivery = createDelivery;
    }

    /**
     * 变量块
     */

    /**
     * 发货单
     */
    private Delivery deliveryEntity;
    private PurchaseOrder orderEntity;
    private List<DeliveryItem> deliveryItemList;
    private List<PurchaseOrderItem> unDeliverItemList = new ArrayList<PurchaseOrderItem>();
    /**
     * 安全发货的状态
     */
    private boolean FULL_DELIVER = false;


    /*** 没有一个合适的异常，可用。**/

    /**
     * @param d
     * @param po
     * @throws BusinessException
     */
    @Override
    public void validation(Delivery d, PurchaseOrder po) throws BusinessException {

        this.deliveryEntity = d;
        this.orderEntity = po;

        String status = d.getStatus();
        //查询订单是否关闭
        if (po.getIsClosed() == DictConsts.CLOSED_YES) {
            throw new BusinessException("该订单被关闭,无法进行发货");
        }
        //发货单是否已关闭
        if (d.getIsClosed() == DictConsts.CLOSED_YES) {
            throw new BusinessException(d.getCode() + "该发货单已被关闭,不可发货");
        }
        // 发货单状态!=0
        if (!status.equals(DictConsts.STATUS_DELIVERY_UNFILLED)) {
            throw new BusinessException(d.getCode() + " 当前状态不可发货");
        }

        // 根据delivery_id查询发货明细
        this.deliveryItemList = deliveryItemMapper.selectList(Wrappers.<DeliveryItem>query().eq("delivery_id", d.getId()));
        if (deliveryItemList.size() == 0) {
            throw new BusinessException("没有明细,无法发货");
        }

        deliveryItemList.stream().forEach(item -> {

            if (item.getQty() <= 0)
                throw new BusinessException(item.getMaterialName() + "实发数量不能小于等于0");
        });


    }

    @Override
    public boolean doDeliver() throws BusinessException {
        // 如果是手工发货单
        if (deliveryEntity.getIsManual() == DictConsts.STATUS_YES) {
            deliveryMapper.update(deliveryEntity, Wrappers.<Delivery>update()
                    .set("status", DictConsts.STATUS_DELIVERY_SENT)
                    .set("gmt_delivery", DateUtils.now())
                    .set("creator", UserUtils.currentUser().getId())
                    .eq("id", deliveryEntity.getId()));
            // 发货
            return FULL_DELIVER;
        }

        //一物一码的或者是需要打印RFID的，生成SN码
        HospitalConfig hospitalConfig = hospitalConfigService.findByHospitalId(deliveryEntity.getHospitalId());
        if (GlobalConsts.YES_STR.equalsIgnoreCase(deliveryEntity.getIsOneThingOneYard()) || (hospitalConfig != null && GlobalConsts.YES_STR.equals(hospitalConfig.getFlagPrintRfid()))) {
            deliveryItemSnService.saveDeliveryItemSns(deliveryEntity.getId(), null);

            if (hospitalConfig != null && GlobalConsts.YES_STR.equals(hospitalConfig.getFlagDeliveryUdiNecessary())) { //UDI必填，判断是否都填了UDI
                List<DeliveryItemSn> allSnList = deliveryItemSnService.list(Wrappers.<DeliveryItemSn>query()
                        .eq("delivery_id", deliveryEntity.getId())
                        .orderByDesc("gmt_modified"));
                Boolean hasEmptyUdit = allSnList.stream().filter(sn -> StringUtils.isEmpty(sn.getUdi())).findAny().isPresent();
                if (hasEmptyUdit) {
                    throw new BusinessException("您需要填写所有的UDI信息，请点击明细列表的最后一个按钮进行UDI的填写");
                }
            }
        }

        Map<String, List<DeliveryItem>> map = deliveryItemList.stream().collect(Collectors.groupingBy(DeliveryItem::getPoItemId));
        List<PurchaseOrderItem> poItems = purchaseOrderItemMapper.selectList(Wrappers.<PurchaseOrderItem>query()
                .eq("po_id", deliveryEntity.getPoId()));
        for (Map.Entry<String, List<DeliveryItem>> entry : map.entrySet()) {
            List<DeliveryItem> deliveryItems = entry.getValue();
            Double sumQty = deliveryItems.stream().mapToDouble(DeliveryItem::getQty).sum();
            DeliveryItem item = deliveryItems.get(0);

            //根据发货单明细的采购单明细id,查询采购明细
            Optional<PurchaseOrderItem> optOpItem = poItems.stream().filter(poItem -> item.getPoItemId().equals(poItem.getId())).findAny();
            if (!optOpItem.isPresent()) {
                continue;
            }
            PurchaseOrderItem pItem = optOpItem.get();
            PurchaseOrderItem orderItem = new PurchaseOrderItem();
            //如果采购数量=已发货数量+本次发货数量 则采购明细全部发货
            if (pItem.getQty() == pItem.getDeliveredQty() + sumQty) {
                //修改采购单发货数量  发货金额  发货状态为 2 全部发货
                orderItem.setDeliveryStatus(DictConsts.STATUS_DELIVERYED)
                        .setDeliveredQty(pItem.getQty())
                        .setDeliveredAmount(item.getSubtotalAmount())
                        .setId(item.getPoItemId());
                pItem.setDeliveryStatus(DictConsts.STATUS_DELIVERYED);
                purchaseOrderItemMapper.updateById(orderItem);
                // 采购数量 > 已发货数量+本次发货数量 则采购明细部分发货
            } else if (pItem.getQty() > pItem.getDeliveredQty() + sumQty) {
                //修改采购单发货数量  发货金额  发货状态为 1 部分发货
                orderItem.setDeliveryStatus(DictConsts.STATUS_PART_DELIVERY)
                        .setDeliveredQty(pItem.getDeliveredQty() + sumQty)
                        .setDeliveredAmount(item.getPrice() * (pItem.getDeliveredQty() + sumQty))
                        .setId(item.getPoItemId());
                pItem.setDeliveryStatus(DictConsts.STATUS_PART_DELIVERY);

                // 添加部分未发货的明细，后面生成新的送货单据会用到
                PurchaseOrderItem unDeliveryItem = new PurchaseOrderItem();
                BeanUtils.copyProperties(orderItem, unDeliveryItem);
                unDeliveryItem.setQty(pItem.getQty() - (pItem.getDeliveredQty() + sumQty));
                unDeliverItemList.add(unDeliveryItem);

                purchaseOrderItemMapper.updateById(orderItem);
            } else {
                throw new BusinessException(MessageFormat.format(item.getMaterialName() + "本次实发数量超过可发数量,可发:{0},本次实发:{1}", (pItem.getQty() - pItem.getDeliveredQty()), sumQty));
            }

            //从耗材表中获取最新的耗材信息
            if (hospitalConfig != null && GlobalConsts.YES_STR.equals(hospitalConfig.getFlagDeliveryMatrUpdate())) {
                HospitalMaterialRequest hospitalMaterialRequest = new HospitalMaterialRequest();
                hospitalMaterialRequest.setCode(item.getMaterialCode())
                        .setHospitalId(deliveryEntity.getHospitalId())
                        .setSupplierId(deliveryEntity.getSupplierId());
                HospitalMaterial hospitalMaterial = hospitalMaterialService.getOneHospitalMaterial(hospitalMaterialRequest);

                if (Objects.nonNull(hospitalMaterial)) {

                    if (StringUtils.isNoneBlank(hospitalMaterial.getCertificateNo())) {
                        deliveryItems.forEach(deliveryItem -> deliveryItem.setCertificateNo(hospitalMaterial.getCertificateNo()));
                    }
                    if (StringUtils.isNoneBlank(hospitalMaterial.getMaterialName())) {
                        deliveryItems.forEach(deliveryItem -> deliveryItem.setMaterialName(hospitalMaterial.getMaterialName())
                        );
                    }
                    if (StringUtils.isNoneBlank(hospitalMaterial.getMaterialSpec())) {
                        deliveryItems.forEach(deliveryItem -> deliveryItem.setMaterialSpec(hospitalMaterial.getMaterialSpec())
                        );
                    }
                    if (StringUtils.isNoneBlank(hospitalMaterial.getManufacturerName())) {
                        deliveryItems.forEach(deliveryItem -> deliveryItem.setManufacturerName(hospitalMaterial.getManufacturerName())
                        );
                    }
                }

            }

            //  将当前发货明细加入采购单的,修改他们的已发数量
            this.deliveryItemMapper.update(null, Wrappers.<DeliveryItem>update()
                    .set("delivered_qty", orderItem.getDeliveredQty())
                    .set("certificate_no", deliveryItems.get(0).getCertificateNo())
                    .set("material_name", deliveryItems.get(0).getMaterialName())
                    .set("material_spec", deliveryItems.get(0).getMaterialSpec())
                    .set("manufacturer_name", deliveryItems.get(0).getManufacturerName())
                    .eq("po_item_id", item.getPoItemId()));
        }

        long count = poItems.stream().filter(poItem -> poItem.getDeliveryStatus().equals(DictConsts.STATUS_UNDELIVERY) || poItem.getDeliveryStatus().equals(DictConsts.STATUS_PART_DELIVERY)).count();
        if (count == 0) {
            FULL_DELIVER = true;

        } else {

        }
        //  直接返回
        return FULL_DELIVER;
    }

    @Override
    public void fullDeliver() {
        if (!FULL_DELIVER) { //部分发货直接返回了
            return;
        }
        /**
         * 完全发货触发关闭剩下的未发货订单。
         */

        //查询该采购订单是否还有未发货的发货单，如果有关闭
        List<Delivery> deliverieList = deliveryMapper.selectList(Wrappers.<Delivery>query().eq("po_id", deliveryEntity.getPoId())
                .eq("po_code", deliveryEntity.getPoCode())
                .eq("hospital_id", deliveryEntity.getHospitalId())
                .eq("status", DictConsts.STATUS_DELIVERY_UNFILLED)
                .eq("is_closed", DictConsts.STATUS_NO)
                .ne("id", deliveryEntity.getId())
        );
        if (deliverieList.size() > 0) {
            //关闭待发货单 关闭原因已全部发货
            // deliverieList.forEach(o -> o.setIsClosed(1).setClosedReason("[点击发货]订单已全部发货，关闭剩下待发货单!"));
            // this.updateBatchById(deliverieList);
            deliveryMapper.update(null, Wrappers.<Delivery>update().set("is_closed", 1).set("closed_reason", "[点击发货]订单已全部发货，关闭剩下待发货单!")
                    .eq("po_id", deliveryEntity.getPoId())
                    .eq("po_code", deliveryEntity.getPoCode())
                    .eq("hospital_id", deliveryEntity.getHospitalId())
                    .eq("status", DictConsts.STATUS_DELIVERY_UNFILLED)
                    .eq("is_closed", DictConsts.STATUS_NO)
                    .ne("id", deliveryEntity.getId()));
        }
        log.info("[发货-全部发货] \t 发货单号:{} 采购单号:{}", deliveryEntity.getCode(), deliveryEntity.getPoCode());
    }

    @Override
    public void partDeliver() {
        if (FULL_DELIVER) { //完全发货直接返回了
            return;
        }
        /**
         * 根据采购单id查询采购订单信息
         * //生成未发货的商品发货单
         */
        // 生成单号
        String code = createDelivery.generateCode(orderEntity.getHospitalId(), orderEntity.getCode());
        DeliveryDTO dDto = new DeliveryDTO();
        BeanUtils.copyProperties(orderEntity, dDto);
        dDto.setIsManual(DictConsts.STATUS_NO)
                .setCode(code)
                .setStatus(DictConsts.STATUS_DELIVERY_UNFILLED);
        // 流程：部分发货 且待发货明细为空
        if (!FULL_DELIVER && unDeliverItemList.isEmpty()) {
            throw new BusinessException("没有需要发货的采购明细，生成新的发货单失败！请检查");
        }

        List<DeliveryItemDTO> itemDtos = Lists.newArrayListWithCapacity(unDeliverItemList.size());
        BeanUtils.copyProperties(unDeliverItemList, itemDtos);
        // 发货实体 Set完毕
        dDto.setItems(itemDtos);
        // 创建发货单
        createDelivery.create(dDto);
    }

    @Override
    public PurchaseOrder updateStatusPurchaseOrder(PurchaseOrder po) {
        if (FULL_DELIVER) { // 完全发货
            po.setDeliveryStatus(DictConsts.STATUS_DELIVERYED).setStatus(DictConsts.STATUS_SUPPLIER_DELIVERYED);
        } else {
            po.setDeliveryStatus(DictConsts.STATUS_PART_DELIVERY).setStatus(DictConsts.STATUS_SUPPLIER_DELIVERYED);
        }
        purchaseOrderMapper.updateById(po);
        return po;
    }

    @Override
    public Delivery updateStatusDelivery(Delivery d) {
        Double deliveryAmount = deliveryItemList.stream().mapToDouble(DeliveryItem::getSubtotalAmount).sum();
        // 更新 发货单 为已经发货状态 1
        d.setDeliveryAmount(deliveryAmount)
                .setStatus(DictConsts.STATUS_DELIVERY_SENT)
                .setGmtDelivery(DateUtils.now())
                .setCreator(UserUtils.currentUser().getNickname());
        deliveryMapper.updateById(d);
        return d;
    }

    @Override
    public void savePurchaseProcess() {
        //添加到订单业务进程表
        PurchaseProcess purchaseProcess = new PurchaseProcess();
        purchaseProcess.setHospitalId(deliveryEntity.getHospitalId())
                .setHospitalName(deliveryEntity.getHospitalName())
                .setSupplierId(deliveryEntity.getSupplierId())
                .setSupplierName(deliveryEntity.getSupplierName())
                .setPoId(deliveryEntity.getPoId())
                .setGmtCreate(new Date());
        if (FULL_DELIVER) {
            purchaseProcess.setContent(deliveryEntity.getPoCode() + "订单全部发货").setType(DictConsts.TYPE_SUPPLIER_ALL_DELIVERYED);
        } else {
            purchaseProcess.setContent(deliveryEntity.getPoCode() + "订单部分发货").setType(DictConsts.TYPE_SUPPLIER_ANY_DELIVERYED);
        }
        //保存订单进程
        purchaseProcessMapper.insert(purchaseProcess);
    }

}

